home *** CD-ROM | disk | FTP | other *** search
/ Sun Solutions 2000 #2 / Sun Solutions CD (Volume 2 2000)(Special Focus - Java Technologies)(Disc 1).ISO / products / Software / TornadoLabsLimited / book / KeyNavigateTest / KeyNavigateTest.java < prev    next >
Text File  |  2000-01-20  |  16KB  |  507 lines

  1. /*
  2. //*****************************************************************************
  3. /*
  4. *    @(#) KeyNavigateTest.java
  5. *
  6. *    Project:        Java 3D Programming
  7. *    Client:        Manning Publications
  8. *
  9. *    Copyright (c) 1999 Daniel Selman 
  10. *    All Rights Reserved.
  11. *
  12. *    @author Daniel Selman: dselman@tornadolabs.com
  13. */
  14. //*****************************************************************************
  15.  
  16. import java.applet.Applet;
  17. import java.awt.*;
  18. import java.awt.event.*;
  19. import java.awt.image.*;
  20.  
  21. import javax.media.j3d.*;
  22. import javax.vecmath.*;
  23.  
  24. import com.sun.j3d.utils.applet.MainFrame;
  25. import com.sun.j3d.utils.geometry.*;
  26. import com.sun.j3d.utils.image.*;
  27.  
  28. import com.tornadolabs.dselman.j3d.book.*;
  29.  
  30. // this example creates a DOOM style world.
  31. // the map for the world is loaded from a GIF
  32. // image, where colors of pixels are converted
  33. // to objects in the 3D world.
  34. // Immersive, first person keyboard navigation
  35. // is implemented along with:
  36. // - simple animated textures (cheat using Tramsform3D)
  37. // - grid based collision detection
  38. // - moving "guards"
  39. // - transparent objects with semi-transparent texture images
  40. //
  41. // GIF images for 3 maps are supplied: small, large and huge.
  42. //
  43. class KeyNavigateTest extends Java3dApplet implements CollisionDetector
  44. {
  45.     private static int                 m_kWidth = 300;
  46.     private static int                 m_kHeight = 300;
  47.     
  48.     private static final String    m_szMapName = new String( "small_map.gif" );
  49.     
  50.     private float                        FLOOR_WIDTH = 256;
  51.     private float                        FLOOR_LENGTH = 256;
  52.     
  53.     // these are the "magic" colors we look for in the GIF image
  54.     private final Color                m_ColorWall = new Color( 0,0,0 );
  55.     private final Color                m_ColorGuard = new Color( 255,0,0 );
  56.     private final Color                m_ColorLight = new Color( 255,255,0 );
  57.     private final Color                m_ColorBookcase = new Color( 0,255,0 );
  58.     private final Color                m_ColorWater = new Color( 0,0,255 );
  59.     
  60.     // size, in world coordinates, of a single pixel in the map
  61.     private Vector3d                    m_MapSquareSize = null;
  62.     
  63.     // shared appearances to minimize memory for textures
  64.     private Appearance                m_WallAppearance = null;
  65.     private Appearance                m_GuardAppearance = null;
  66.     private Appearance                m_BookcaseAppearance = null;
  67.     private Appearance                m_WaterAppearance = null;
  68.     
  69.     // the GIF image that is the map for the world
  70.     private BufferedImage            m_MapImage = null;
  71.  
  72.     // vertical levels for the world 
  73.     // - the viewer is at y=0
  74.     private final double                m_kFloorLevel = -20;
  75.     private final double                m_kCeilingHeight = 50;
  76.     private final double                m_kCeilingLevel = m_kFloorLevel + m_kCeilingHeight;
  77.         
  78.     private Vector3d                    m_Translation = new Vector3d();    
  79.             
  80.     public KeyNavigateTest()
  81.     {
  82.         initJava3d();
  83.     }
  84.     
  85.     protected void addCanvas3D( Canvas3D c3d )
  86.     {
  87.         add( c3d );
  88.         doLayout();
  89.     }
  90.     
  91.     protected double getScale()
  92.     {
  93.         return 0.05;
  94.     }
  95.     
  96.     protected int getCanvas3dWidth( Canvas3D c3d )
  97.     {
  98.         return m_kWidth - 10;
  99.     }
  100.     
  101.     protected int getCanvas3dHeight( Canvas3D c3d )
  102.     {
  103.         return m_kHeight - 10;
  104.     }
  105.     
  106.     // edit the positions of the clipping
  107.     // planes so we don't clip on the front 
  108.     // plane prematurely
  109.     protected double getBackClipDistance()
  110.     {
  111.         return 20.0;
  112.     }
  113.     
  114.     protected double getFrontClipDistance()
  115.     {
  116.         return 0.1;
  117.     }
  118.  
  119.     
  120.     // we create 2 TransformGroups above the ViewPlatform:
  121.     // the first merely applies a scale, while the is 
  122.     // manipulated using the KeyBehavior
  123.     public TransformGroup[] getViewTransformGroupArray()
  124.     {
  125.         TransformGroup[] tgArray = new TransformGroup[2];
  126.         tgArray[0] = new TransformGroup();
  127.         tgArray[1] = new TransformGroup();
  128.         
  129.         Transform3D t3d = new Transform3D();
  130.         t3d.setScale( getScale() );
  131.         t3d.invert();
  132.         tgArray[0].setTransform( t3d );        
  133.                                         
  134.         // ensure the Behavior can access the TG
  135.         tgArray[1].setCapability( TransformGroup.ALLOW_TRANSFORM_WRITE );
  136.         
  137.         // create the KeyBehavior and attach
  138.         KeyCollisionBehavior keyBehavior = new KeyCollisionBehavior( tgArray[1], this );
  139.         keyBehavior.setSchedulingBounds( getApplicationBounds() );
  140.         keyBehavior.setMovementRate( 0.7 );
  141.         tgArray[1].addChild( keyBehavior );
  142.         
  143.         return tgArray;
  144.     }
  145.  
  146.     
  147.     protected BranchGroup createSceneBranchGroup()
  148.     {
  149.         BranchGroup objRoot = super.createSceneBranchGroup();
  150.         
  151.         TransformGroup objTrans = new TransformGroup();
  152.         objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
  153.         objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
  154.         
  155.         createMap( objTrans );
  156.         createFloor( objTrans );
  157.         createCeiling( objTrans );
  158.         
  159.         objRoot.addChild( objTrans );
  160.  
  161.         return objRoot;
  162.     }
  163.         
  164.     public Group createFloor( Group g )
  165.     {    
  166.         System.out.println( "Creating floor" );
  167.  
  168.         Group floorGroup = new Group();
  169.         Land floorTile = null;
  170.  
  171.         // use a shared Appearance so we only store 1 copy of the texture
  172.         Appearance app = new Appearance();        
  173.         g.addChild( floorGroup );
  174.         
  175.         final double kNumTiles = 6;
  176.         
  177.         for( double x = -FLOOR_WIDTH + FLOOR_WIDTH/(2 * kNumTiles); x < FLOOR_WIDTH; x = x + FLOOR_WIDTH/kNumTiles )
  178.         {
  179.             for( double z = -FLOOR_LENGTH + FLOOR_LENGTH/(2 * kNumTiles); z < FLOOR_LENGTH; z = z + FLOOR_LENGTH/kNumTiles )
  180.             {
  181.                 floorTile = new Land( this, g, ComplexObject.GEOMETRY | ComplexObject.TEXTURE );
  182.                 floorTile.createObject( app, 
  183.                                                 new Vector3d(x,m_kFloorLevel,z), 
  184.                                                 new Vector3d( FLOOR_WIDTH/(2*kNumTiles),1, FLOOR_LENGTH/(2*kNumTiles)),
  185.                                                 "floor.gif", null, null );
  186.             }
  187.         }
  188.         
  189.         return floorGroup;
  190.     }
  191.     
  192.     public Group createCeiling( Group g )
  193.     {    
  194.         System.out.println( "Creating ceiling" );
  195.  
  196.         Group ceilingGroup = new Group();
  197.         Land ceilingTile = null;
  198.     
  199.         // because we are technically viewing the ceiling from below
  200.         // we want to switch the normals using a PolygonAttributes.
  201.         Appearance app = new Appearance();
  202.         app.setPolygonAttributes( new PolygonAttributes( PolygonAttributes.POLYGON_FILL, PolygonAttributes.CULL_NONE, 0, false ) );
  203.         
  204.         g.addChild( ceilingGroup );
  205.         
  206.         final double kNumTiles = 6;
  207.         
  208.         for( double x = -FLOOR_WIDTH + FLOOR_WIDTH/(2 * kNumTiles); x < FLOOR_WIDTH; x = x + FLOOR_WIDTH/kNumTiles )
  209.         {
  210.             for( double z = -FLOOR_LENGTH + FLOOR_LENGTH/(2 * kNumTiles); z < FLOOR_LENGTH; z = z + FLOOR_LENGTH/kNumTiles )
  211.             {
  212.                 ceilingTile = new Land( this, g, ComplexObject.GEOMETRY | ComplexObject.TEXTURE );
  213.                 ceilingTile.createObject(     app, 
  214.                                                     new Vector3d(x,m_kCeilingLevel,z), 
  215.                                                     new Vector3d( FLOOR_WIDTH/(2*kNumTiles),1, FLOOR_LENGTH/(2*kNumTiles)) , 
  216.                                                     "ceiling.gif", null, null );
  217.             }
  218.         }
  219.         
  220.         return ceilingGroup;
  221.     }
  222.     
  223.     Point3d convertToWorldCoordinate( int nPixelX, int nPixelY )
  224.     {
  225.         Point3d point3d = new Point3d();
  226.         Vector3d squareSize = getMapSquareSize();
  227.                 
  228.         // range from 0 to 1
  229.         point3d.x = nPixelX * squareSize.x;
  230.         point3d.x -= FLOOR_WIDTH;
  231.         
  232.         point3d.z = nPixelY * squareSize.z;
  233.         point3d.z -= FLOOR_LENGTH;
  234.         
  235.         point3d.y = 0;
  236.                 
  237.         return point3d;
  238.     }
  239.     
  240.  
  241.     Point3d convertToWorldCoordinatesPixelCenter( int nPixelX, int nPixelY )
  242.     {
  243.         Point3d point3d = convertToWorldCoordinate( nPixelX, nPixelY );
  244.         Vector3d squareSize = getMapSquareSize();
  245.  
  246.         point3d.x += squareSize.x/2;
  247.         point3d.z += squareSize.z/2;
  248.                 
  249.         return point3d;
  250.     }
  251.  
  252.  
  253.     Point2d convertToMapCoordinate( Vector3d worldCoord )
  254.     {
  255.         Point2d point2d = new Point2d();
  256.         
  257.         Vector3d squareSize = getMapSquareSize();
  258.                 
  259.         point2d.x = (worldCoord.x + FLOOR_WIDTH)/ squareSize.x;
  260.         point2d.y = (worldCoord.z + FLOOR_LENGTH)/ squareSize.z;        
  261.         
  262.         return point2d;                    
  263.     }
  264.     
  265.     public boolean isCollision( Transform3D t3d, boolean bViewSide )
  266.     {
  267.         // get the translation
  268.         t3d.get( m_Translation );
  269.         
  270.         // we need to scale up by the scale that was
  271.         // applied to the root TG on the view side of the scenegraph
  272.         if( bViewSide != false )
  273.             m_Translation.scale( 1.0 / getScale() );
  274.         
  275.         Vector3d mapSquareSize = getMapSquareSize();
  276.         
  277.         // first check that we are still inside the "world"
  278.         if(     m_Translation.x < -FLOOR_WIDTH + mapSquareSize.x ||
  279.                 m_Translation.x > FLOOR_WIDTH - mapSquareSize.x ||
  280.                 m_Translation.y < -FLOOR_LENGTH + mapSquareSize.y ||
  281.                 m_Translation.y > FLOOR_LENGTH - mapSquareSize.y )
  282.                     return true;
  283.                     
  284.         if( bViewSide != false )
  285.             // then do a pixel based look up using the map
  286.             return isCollision( m_Translation );
  287.             
  288.         return false;
  289.     }
  290.     
  291.     // returns true if the given x,z location in the world corresponds to a wall section
  292.     protected boolean isCollision( Vector3d worldCoord )
  293.     {
  294.         Point2d point = convertToMapCoordinate( worldCoord );
  295.         int nImageWidth = m_MapImage.getWidth();
  296.         int nImageHeight = m_MapImage.getHeight();
  297.  
  298.         // outside of image
  299.         if( point.x < 0 || point.x >= nImageWidth ||
  300.              point.y < 0 || point.y >= nImageHeight )
  301.              return true;
  302.  
  303.         
  304.         Color color = new Color( m_MapImage.getRGB( (int) point.x, (int) point.y ) );
  305.         
  306.         // we can't walk through walls or bookcases
  307.         return( color.hashCode() == m_ColorWall.hashCode() || color.hashCode() == m_ColorBookcase.hashCode() );
  308.     }
  309.     
  310.     public Group createMap( Group g )
  311.     {    
  312.         System.out.println( "Creating map items" );
  313.         
  314.         Group mapGroup = new Group();
  315.         g.addChild( mapGroup );
  316.         
  317.         Texture tex = new TextureLoader( m_szMapName, this).getTexture();
  318.         m_MapImage = ((ImageComponent2D) tex.getImage( 0 )).getImage();
  319.         
  320.         float imageWidth = m_MapImage.getWidth();
  321.         float imageHeight = m_MapImage.getHeight();
  322.         
  323.         FLOOR_WIDTH = imageWidth * 8;
  324.         FLOOR_LENGTH = imageHeight * 8;
  325.         
  326.         for( int nPixelX = 1; nPixelX < imageWidth-1; nPixelX++ )
  327.         {
  328.             for( int nPixelY = 1; nPixelY < imageWidth-1; nPixelY++ )
  329.                 createMapItem( mapGroup, nPixelX, nPixelY );
  330.                 
  331.             float percentDone = 100 * (float) nPixelX / (float) (imageWidth-2);
  332.             System.out.println( "   " + (int) (percentDone) + "%" );
  333.         }
  334.         
  335.         createExternalWall( mapGroup );
  336.         
  337.         return mapGroup;
  338.     }
  339.     
  340.     void createMapItem( Group mapGroup, int nPixelX, int nPixelY )
  341.     {
  342.         Color color = new Color( m_MapImage.getRGB( (int) nPixelX, (int) nPixelY ) );
  343.         
  344.         if( color.hashCode() == m_ColorWall.hashCode() )
  345.             createWall( mapGroup, nPixelX, nPixelY );
  346.             
  347.         else if( color.hashCode() == m_ColorGuard.hashCode() )
  348.             createGuard( mapGroup, nPixelX, nPixelY );
  349.             
  350.         else if( color.hashCode() == m_ColorLight.hashCode() )
  351.             createLight( mapGroup, nPixelX, nPixelY );
  352.             
  353.         else if( color.hashCode() == m_ColorBookcase.hashCode() )
  354.             createBookcase( mapGroup, nPixelX, nPixelY );
  355.             
  356.         else if( color.hashCode() == m_ColorWater.hashCode() )
  357.             createWater( mapGroup, nPixelX, nPixelY );
  358.     }
  359.     
  360.     Vector3d getMapSquareSize()
  361.     {
  362.         if( m_MapSquareSize == null )
  363.         {
  364.             double imageWidth = m_MapImage.getWidth();
  365.             double imageHeight = m_MapImage.getHeight();
  366.             m_MapSquareSize = new Vector3d( 2 * FLOOR_WIDTH / imageWidth, 0, 2 * FLOOR_LENGTH / imageHeight );
  367.         }
  368.         
  369.         return m_MapSquareSize;
  370.     }
  371.     
  372.     void createWall( Group mapGroup, int nPixelX, int nPixelY )
  373.     {
  374.         Point3d point = convertToWorldCoordinatesPixelCenter( nPixelX, nPixelY );
  375.                 
  376.         if( m_WallAppearance == null )
  377.             m_WallAppearance = new Appearance();
  378.             
  379.         Vector3d squareSize = getMapSquareSize();
  380.                 
  381.         Cuboid wall = new Cuboid( this, mapGroup, ComplexObject.GEOMETRY | ComplexObject.TEXTURE );
  382.         wall.createObject(     m_WallAppearance, 
  383.                                     new Vector3d( point.x, m_kFloorLevel, point.z ),
  384.                                     new Vector3d( squareSize.x/2, m_kCeilingHeight/2, squareSize.z/2) ,
  385.                                     "wall.gif", null, null );
  386.     }
  387.     
  388.  
  389.     void createExternalWall( Group mapGroup )
  390.     {
  391.         Vector3d squareSize = getMapSquareSize();
  392.                 
  393.         Appearance app = new Appearance();
  394.         app.setColoringAttributes( new ColoringAttributes( new Color3f( 132f/255f,0,66f/255f ), ColoringAttributes.FASTEST ) );
  395.  
  396.         int imageWidth = m_MapImage.getWidth();
  397.         int imageHeight = m_MapImage.getHeight();
  398.  
  399.         Point3d topLeft = convertToWorldCoordinatesPixelCenter( 0,0 );
  400.         Point3d bottomRight = convertToWorldCoordinatesPixelCenter( imageWidth-1, imageHeight-1 );
  401.         
  402.         // top
  403.         Cuboid wall = new Cuboid( this, mapGroup, ComplexObject.GEOMETRY );
  404.         wall.createObject(     app, 
  405.                                     new Vector3d( 0, m_kFloorLevel, topLeft.z ),
  406.                                     new Vector3d( squareSize.x * imageWidth/2, m_kCeilingHeight/2, squareSize.z/2 ),
  407.                                     null, null, null );
  408.                                     
  409.         // bottom
  410.         wall = new Cuboid( this, mapGroup, ComplexObject.GEOMETRY );
  411.         wall.createObject(     app, 
  412.                                     new Vector3d( 0, m_kFloorLevel, bottomRight.z ),
  413.                                     new Vector3d( squareSize.x * imageWidth/2, m_kCeilingHeight/2, squareSize.z/2 ),
  414.                                     null, null, null );
  415.                                     
  416.         // left
  417.         wall = new Cuboid( this, mapGroup, ComplexObject.GEOMETRY );
  418.         wall.createObject(     app, 
  419.                                     new Vector3d( topLeft.x, m_kFloorLevel, 0 ),
  420.                                     new Vector3d( squareSize.x/2, m_kCeilingHeight/2, squareSize.z * imageHeight/2) ,
  421.                                     null, null, null );
  422.                                     
  423.         // right
  424.         wall = new Cuboid( this, mapGroup, ComplexObject.GEOMETRY );
  425.         wall.createObject(     app, 
  426.                                     new Vector3d( bottomRight.x, m_kFloorLevel, 0 ),
  427.                                     new Vector3d( squareSize.x/2, m_kCeilingHeight/2, squareSize.z * imageHeight/2) ,
  428.                                     null, null, null );
  429.     }
  430.  
  431.  
  432.     void createGuard( Group mapGroup, int nPixelX, int nPixelY )
  433.     {
  434.         Point3d point = convertToWorldCoordinatesPixelCenter( nPixelX, nPixelY );
  435.         
  436.         if( m_GuardAppearance == null )
  437.             m_GuardAppearance = new Appearance();
  438.         
  439.         Vector3d squareSize = getMapSquareSize();
  440.         
  441.         Guard guard = new Guard( this, mapGroup, ComplexObject.GEOMETRY, this );
  442.         guard.createObject(     m_GuardAppearance,
  443.                                     new Vector3d( point.x, (m_kFloorLevel+m_kCeilingLevel)/2, point.z ),
  444.                                     new Vector3d( 1, 1, 1 ),
  445.                                     null, null, null );
  446.     }
  447.  
  448.     void createLight( Group mapGroup, int nPixelX, int nPixelY )
  449.     {
  450.         Point3d point = convertToWorldCoordinatesPixelCenter( nPixelX, nPixelY );
  451.             
  452.         // we do not share an Appearance for lights
  453.         // or they all animate in synch...
  454.         final double lightHeight = m_kCeilingHeight/1.5;
  455.         
  456.         Light light = new Light( this, mapGroup, ComplexObject.GEOMETRY | ComplexObject.TEXTURE );
  457.         light.createObject(     new Appearance(), 
  458.                                     new Vector3d( point.x, m_kFloorLevel+lightHeight/2, point.z ),
  459.                                     new Vector3d( 5, lightHeight, 5) ,
  460.                                     "light.gif", null, null );
  461.     }
  462.     
  463.     void createWater( Group mapGroup, int nPixelX, int nPixelY )
  464.     {
  465.         Point3d point = convertToWorldCoordinatesPixelCenter( nPixelX, nPixelY );
  466.         
  467.         if( m_WaterAppearance == null )
  468.         {
  469.             m_WaterAppearance = new Appearance();
  470.             m_WaterAppearance.setPolygonAttributes( new PolygonAttributes( PolygonAttributes.POLYGON_FILL, PolygonAttributes.CULL_NONE, 0, false ) );
  471.             m_WaterAppearance.setTransparencyAttributes( new TransparencyAttributes( TransparencyAttributes.BLENDED, 1.0f ) );
  472.             m_WaterAppearance.setTextureAttributes( new TextureAttributes( TextureAttributes.REPLACE, new Transform3D(), new Color4f(0,0,0,1), TextureAttributes.FASTEST ) );
  473.  
  474.         }
  475.         
  476.         Land water = new Land( this, mapGroup, ComplexObject.GEOMETRY | ComplexObject.TEXTURE );
  477.         water.createObject(     m_WaterAppearance, 
  478.                                     new Vector3d( point.x, m_kFloorLevel+0.1, point.z ),
  479.                                     new Vector3d( 40, 1, 40 ) ,
  480.                                     "water.gif", null, null );
  481.     }
  482.  
  483.     void createBookcase( Group mapGroup, int nPixelX, int nPixelY )
  484.     {
  485.         Point3d point = convertToWorldCoordinatesPixelCenter( nPixelX, nPixelY );
  486.         
  487.         if( m_BookcaseAppearance == null )
  488.             m_BookcaseAppearance = new Appearance();
  489.         
  490.         Vector3d squareSize = getMapSquareSize();
  491.         
  492.         Cuboid bookcase = new Cuboid( this, mapGroup, ComplexObject.GEOMETRY | ComplexObject.TEXTURE );
  493.         bookcase.createObject(     m_BookcaseAppearance,
  494.                                         new Vector3d( point.x, m_kFloorLevel, point.z ),
  495.                                         new Vector3d( squareSize.x/2, m_kCeilingHeight/2.7, squareSize.z/2),
  496.                                         "bookcase.gif", null, null );
  497.  
  498.     }
  499.  
  500.     public static void main(String[] args)
  501.     {
  502.         KeyNavigateTest keyTest = new KeyNavigateTest();
  503.         keyTest.saveCommandLineArguments( args );
  504.         
  505.         new MainFrame( keyTest, m_kWidth, m_kHeight );
  506.     }
  507. }